#include "./util.hh"

#include <cstddef>
#include <format>
#include <fstream>
#include <ranges>
#include <stdexcept>
#include <string>
#include <string_view>
#include <vector>

namespace ycspice {

    auto is_white_char(char ch) -> bool {
        return ch == ' ' || ch == '\r' || ch == '\n' || ch == '\a' || ch == '\t';
    }

    std::size_t filesize(std::ifstream& file) {
        file.seekg(0, std::ios::end);
        std::size_t size{ static_cast<std::size_t>(file.tellg()) };
        file.seekg(0, std::ios::beg);
        return size;
    }

    std::string read_file(const std::filesystem::path& file_path) {
        auto infile = std::ifstream{file_path};

        if (!infile.is_open()) {
            throw std::runtime_error{std::format("Open '{}' failed", file_path.string())};
        }

        std::size_t size {filesize(infile)};
        std::string content {};
        content.resize(size + 1);
        infile.read(content.data(), size);
        content.resize(size);

        return content;
    }

    auto read_file_lines(const std::filesystem::path& file_path) -> std::vector<std::string> {
        auto ifstram = std::ifstream{file_path};
        if (!ifstram.is_open()) {
            throw std::runtime_error{std::format("Open '{}' failed", file_path.string())};
        }

        std::string line;
        auto lines = std::vector<std::string>{};
        while (std::getline(ifstram, line)) {
            lines.emplace_back(string_lowercase(line));
        } 

        return lines;
    }

    auto string_lowercase(std::string_view input) -> std::string {
        return input 
            | std::ranges::views::transform([] (char c) -> char {
                if (c >= 'A' && c <= 'Z') {
                    return c + ('a' - 'A');
                } else {
                    return c;
                }
            }) 
            | std::ranges::to<std::string>();
    }

    auto string_trim(std::string_view s) -> std::string_view {
        s.remove_prefix(std::find_if(s.begin(), s.end(), [](char ch){ return !is_white_char(ch); }) - s.begin());
        s.remove_suffix(std::find_if(s.rbegin(), s.rend(), [](char ch){ return !is_white_char(ch); }) - s.rbegin());
        return s;
    }


    auto string_split_space(std::string_view s) -> std::vector<std::string_view> {
        const auto white_chars = std::string_view{" \a\b\t\n\r"};

        s = string_trim(s);
        if (s.empty()) 
            return {};

        auto begin_pos = 0;
        auto result = std::vector<std::string_view>{};
        while (true) {
            auto first_white_pos = s.find_first_of(white_chars, begin_pos);
            if (first_white_pos == std::string_view::npos) {
                result.emplace_back(s.substr(begin_pos));
                break;
            }

            result.emplace_back(s.substr(begin_pos, first_white_pos - begin_pos));

            begin_pos = s.find_first_not_of(white_chars, first_white_pos + 1);
        }

        return result;
        // std::vector<std::string_view> tokens;
        // for (auto token : trim_s | std::views::split(' ')) { 
        //     auto token_str = std::string_view{token.begin(), token.end()};
        //     if (token_str.empty()) {
        //         continue;
        //     }
        //     tokens.emplace_back(token_str);
        // }
        // return tokens;
    }


}